package com.nebula.core.dsl.operation;

import com.alibaba.druid.sql.ast.expr.SQLIdentifierExpr;
import com.alibaba.druid.sql.ast.statement.SQLSelectItem;
import com.nebula.core.client.NebulaSession;
import com.nebula.core.dsl.WhereStatement;
import com.nebula.core.dsl.builder.FetchStatementBuilder;
import com.nebula.core.dsl.builder.StatementBuilder;
import com.nebula.core.entity.vo.Edge;
import com.vesoft.nebula.client.graph.data.ResultSet;
import org.apache.commons.lang3.StringUtils;
import org.springframework.util.CollectionUtils;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

import static com.nebula.core.constant.Constant.QUOTED_LITERAL_FORMAT;

/**
 * fetch属性查询操作类
 * <p>
 * FETCH PROP ON {<tag_name>[, tag_name ...] | *}
 * <vid> [, vid ...]
 * YIELD [DISTINCT] <return_list> [AS <alias>]
 * <p>
 * <p>
 * FETCH PROP ON <edge_type> <src_vid> -> <dst_vid>[@<rank>] [, <src_vid> -> <dst_vid> ...]
 * YIELD <output>;
 *
 * @author zhangtao
 */
public class FetchOperation implements FetchStatementBuilder.FetchStatement {
    private List<Object> vidList = new ArrayList<>();
    private String reference;
    private List<Edge> edgeList = new ArrayList<>();
    private List<String> typeList = new ArrayList<>();
    private WhereStatement where;
    private List<SQLSelectItem> selectList = new ArrayList<SQLSelectItem>();
    private NebulaSession nebulaSession;

    private FetchOperation() {
    }

    private FetchOperation(NebulaSession nebulaSession) {
        this.nebulaSession = nebulaSession;
    }

    protected static FetchStatementBuilder.FetchStart create() {
        return new FetchOperation();
    }

    protected static FetchStatementBuilder.FetchStart create(NebulaSession nebulaSession) {
        return new FetchOperation(nebulaSession);
    }

    @Override
    public FetchStatementBuilder.FetchTypeOut on(String... types) {
        this.typeList.addAll(Arrays.stream(types).collect(Collectors.toList()));
        return this;
    }

    @Override
    public FetchStatementBuilder.FetchFromTagOut from(Object... vids) {
        this.vidList.addAll(Arrays.stream(vids).collect(Collectors.toList()));
        return this;
    }

    @Override
    public FetchStatementBuilder.FetchFromTagOut fromPipe(String reference) {
        this.reference = reference;
        return this;
    }

    @Override
    public FetchStatementBuilder.FetchFromEdgeOut from(Edge... edges) {
        this.edgeList.addAll(Arrays.stream(edges).collect(Collectors.toList()));
        return this;
    }

    @Override
    public FetchStatementBuilder.FetchYieldOut yield(String field, String alias) {
        this.selectList.add(new SQLSelectItem(new SQLIdentifierExpr(field), alias));
        return this;
    }

    @Override
    public String build() {
        StringBuilder builder = new StringBuilder("FETCH PROP ON ");

        //拼接边类型
        builder.append(typeList.stream().collect(Collectors.joining(",", " ", " ")));

        //拼接vid
        if (!CollectionUtils.isEmpty(vidList)) {
            builder.append(vidList.stream().map(vid -> {
                if (vid instanceof Integer) {
                    return String.valueOf(vid);
                } else {
                    return String.format(QUOTED_LITERAL_FORMAT, vid);
                }
            }).collect(Collectors.joining(",", " ", " ")));
        }

        //拼接边vid
        if (!CollectionUtils.isEmpty(edgeList)) {
            for (int i = 0; i < edgeList.size(); i++) {
                Edge edge = edgeList.get(i);
                if (i != 0) {
                    builder.append(",");
                }
                builder.append(String.format(QUOTED_LITERAL_FORMAT, edge.getSrcId()));
                builder.append("->");
                builder.append(String.format(QUOTED_LITERAL_FORMAT, edge.getDstId()));
            }
        }

        //拼接引用变量
        if (StringUtils.isNotEmpty(this.reference)) {
            builder.append(reference);
        }

        //拼接返回字段
        if (!CollectionUtils.isEmpty(selectList)) {
            builder.append(" YIELD ");
            for (int i = 0; i < selectList.size(); i++) {
                if (i != 0) {
                    builder.append(",");
                }
                selectList.get(i).output(builder);
            }
        }

        return builder.toString();
    }

    @Override
    public ResultSet excute() {
        return nebulaSession.query(this.build());
    }
}